/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkAssignStmt, ArkField, ArkMethod, Stmt } from "arkanalyzer";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Rule, Defects, MethodMatcher, MatcherTypes, MatcherCallback, ClassMatcher, FieldMatcher, FileMatcher } from '../../Index';
import { RuleListUtil } from "../../utils/common/DefectsList";
import { IssueReport } from "../../model/Defects";

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoNewWrappersCheck');
const gMetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/no-new-wrappers-check.md",
    description: "Do not use String as a constructor."
};

export class NoNewWrappersCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    readonly WRAPPERS_STR: string = "string";
    readonly CREAER_STR: string = "new String";
    readonly CREAERNUM_STR: string = "new Number";
    readonly CREAERBOOL_STR: string = "new Boolean";
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];

    private fieldMatcher: FieldMatcher = {
        matcherType: MatcherTypes.FIELD,
    };

    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };

    private clsMatcher: ClassMatcher = {
        matcherType: MatcherTypes.CLASS,
    };

    private methodMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
        class: [this.clsMatcher],
        file: [this.fileMatcher],
    };

    public registerMatchers(): MatcherCallback[] {
        const methodMatcherCb: MatcherCallback = {
            matcher: this.methodMatcher,
            callback: this.check,
        };
        const fieldMatcherCb: MatcherCallback = {
            matcher: this.fieldMatcher,
            callback: this.check,
        };
        return [methodMatcherCb, fieldMatcherCb];
    }

    public check = (target: ArkMethod | ArkField) => {
        if (target instanceof ArkMethod) {
            const stmts = target.getBody()?.getCfg().getStmts() ?? [];
            for (const stmt of stmts) {
                if (stmt instanceof ArkAssignStmt) {
                    continue;
                }
                let text = stmt.getOriginalText() ?? '';
                if (text != null || text != "") {
                    let textStr = text.split("=")
                    if (textStr) {
                        let textArray = textStr[1]
                        let textString = /new String|new Number|new Boolean/.test(textArray?.trim());
                        if (textString == true) {
                            this.addIssueReport(stmt);
                        }
                    }
                }
            }
        } else {
            if (target instanceof ArkField) {
                let stmts: Stmt[] = target.getInitializer();
                for (const stmt of stmts) {
                    if (stmt instanceof ArkAssignStmt) {
                        continue;
                    }
                    let text = stmt.getOriginalText() ?? '';
                    if (text != null || text != "") {
                        let textStr = text.split("=")
                        if (textStr) {
                            let textArray = textStr[1]
                            let textString = /new String|new Number|new Boolean/.test(textArray?.trim());
                            if (textString == true) {
                                this.addIssueReport(stmt);
                            }
                        }
                    }
                }
            }
        }
    }

    private addIssueReport(stmt: Stmt) {
        const severity = this.rule.alert ?? this.metaData.severity;
        const warnInfo = this.getLineAndColumn(stmt);
        const description = "Do not use " + warnInfo.name + " as a constructor."
        let defect = new Defects(warnInfo.line, warnInfo.startCol, warnInfo.endCol, description, severity, this.rule.ruleId,
            warnInfo.filePath as string, this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defect, undefined));
        RuleListUtil.push(defect)
    }
    private getLineAndColumn(stmt: Stmt) {
        const originPosition = stmt.getOriginPositionInfo();
        const line = originPosition.getLineNo();
        const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
        if (arkFile) {
            const originText = stmt.getOriginalText() ?? '';
            let textString = /new String/.test(originText?.trim());
            let textNumber = /new Number/.test(originText?.trim());
            let textBoolean = /new Boolean/.test(originText?.trim());
            let startCol = originPosition.getColNo();
            if (textString == true) {
                const pos = originText.indexOf(this.CREAER_STR);
                if (pos !== -1) {
                    startCol += pos;
                    const endCol = startCol + this.CREAER_STR.length - 1;
                    const name = "String"
                    const originPath = arkFile.getFilePath();
                    return { line, startCol, endCol, name, filePath: originPath }
                }
            } else if (textNumber == true) {
                const pos = originText.indexOf(this.CREAERNUM_STR);
                if (pos !== -1) {
                    startCol += pos;
                    const endCol = startCol + this.CREAERNUM_STR.length - 1;
                    const name = "Number"
                    const originPath = arkFile.getFilePath();
                    return { line, startCol, endCol, name, filePath: originPath }
                }
            } else if (textBoolean == true) {
                const pos = originText.indexOf(this.CREAERBOOL_STR);
                if (pos !== -1) {
                    startCol += pos;
                    const endCol = startCol + this.CREAERBOOL_STR.length - 1;
                    const name = "Boolean"
                    const originPath = arkFile.getFilePath();
                    return { line, startCol, endCol, name, filePath: originPath }
                }
            }

        } else {
            logger.debug('arkFile is null');
        }
        return { line: -1, startCol: -1, endCol: -1, filePath: '' };
    }
}